
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Random;



public class Bot extends Thread {
        private Socket botSocket;
        private DataOutputStream writer;
        private DataInputStream reader;
               
        private int botID = 0,
                        botChips = 0,
                        botChipsOnStart = 0,
                        botBet = 0,
                        botTotalBet = 0,
                        botSuggestedMaxBet = 0,
                        gameCurrentRound = 1,
                        gameMaxBet = 0;
       
        private double botChance = 0;
       
        private boolean isGameOpen = false,
                        botMovesEnabled = true,
                        connectionOk = false;
       
        private boolean[] botPossibleMoves;
       
        private String botName = "BadugiBot",
                        action = "";
       
        private String[][] botCards;

       
        /**
         * Constructor.
         * @param host Server's IP address.
         * @param port Server's port.
         */
        public Bot(String host, int port) {
                botName = "BOT";
                connectToServer(host, port);    
        }

       
        /**
         * Connects and checks if connection was successful.
         * @param host Server's IP address.
         * @param port Server's port.
         */
        private void connectToServer(String host, int port) {
                try {
                        botSocket = new Socket(host, port);

                        reader = new DataInputStream(botSocket.getInputStream());
                        writer = new DataOutputStream(botSocket.getOutputStream());

                        Thread th = new Thread(this);
                        th.start();

                        sendRequest("setPlayerName " + botName);
                       
                        try {
                                Thread.sleep(1000);
                        } catch (Exception e) {}
                       
                        if (!connectionOk) {
                                System.out.println(" > Bot cannot connect to server.");
                                th.interrupt();
                                System.exit(-1);
                        } else {
                                System.out.println(" > Bot successfully connected to server :)");
                        }
                } catch (IOException e) {
                        System.out.println(" > Cannot connect to '" + host + "' (port: " + port + ")");
                        System.exit(-1);
                }
        }
       
       
        /**
         * Sends a single request to server.
         * @param message Request to send.
         */
        private void sendRequest(String message) {
                try {
                        writer.writeUTF(message);
                } catch (IOException e) {
                        System.out.println(" ~ I can't send a request.");
                }
        }      
       
       
        /**
         * @param tab An array with splitted by ' ' server's response.
         * Checks what moves are enabled to be made.
         */
        private void botPossibleMoves(String[] tab) {
                boolean check = false, bet = false, call = false, raise = false, allIn = false;
                boolean[] possibleActions = new boolean[]{check, bet, call, raise, allIn};
                int index = 0;
               
                for (int i = tab.length-5; i < tab.length; i++) {
                        possibleActions[index] = Boolean.parseBoolean(tab[i]);
                       
                        index += 1;
                }
               
                botPossibleMoves = possibleActions;
        }
       
       
        /**
         * Decides what move to make depending on strength of BOT's hand.
         */
        private void itsBotsMove() {
                calculateChanceAndMaxBet();
                boolean[] m = botPossibleMoves;
                int amount = 0;
               
                Random rand = new Random();
               
                try {
                        amount = rand.nextInt((int)(Math.round((botSuggestedMaxBet - botBet)*(botChance/100) + 0.5d))) + 1;
                } catch (Exception e) {
                        amount = 1;
                }


                if (botChance < 4) {
                        if (gameCurrentRound == 1)
                                action = "fold";
                        else
                                action = "allIn";
                       
                } else if (botChance < 10) {
                        if (m[0])
                                action = "check";
                        else if (m[2] && gameMaxBet <= botChips*0.1)
                                action = "call";
                        else
                                action = "allIn";
                       
                } else if (botChance >= 10 && botChance < 20) {
                        amount = (int)(Math.round(amount/4 + 0.5d));
                       
                       
                        if (m[1])
                                action = "bet 1";
                        else if (m[0])
                                action = "check";
                        else if (m[2] && (gameMaxBet <= botSuggestedMaxBet || gameMaxBet <= botChips*0.1))
                                action = "call";
                        else
                                action = "allIn";
                       
                } else if (botChance >= 20 && botChance <= 35) {
                        amount = (int)(Math.round(amount/2 + 0.5d));

                       
                        if (m[1])
                                action = "bet " + amount;
                        else if (m[3] && gameMaxBet + amount <= botSuggestedMaxBet)
                                action = "raise " + amount;
                        else if (m[3] && gameMaxBet + 1 <= botSuggestedMaxBet)
                                action = "raise 1";
                        else
                                action = "call";
                       
                } else {
                        int amount_half = (int)(Math.round(amount/2 + 0.5d));
                       
                        if (m[1])
                                action = "bet " + amount;
                        else if (m[3] && gameMaxBet + amount <= botSuggestedMaxBet)
                                action = "raise " + amount;
                        else if (m[3] && gameMaxBet + amount_half <= botSuggestedMaxBet)
                                action = "raise " + amount_half;
                        else if (m[3] && gameMaxBet + 1 <= botSuggestedMaxBet)
                                action = "raise 1";
                        else
                                action = "call";
                }
               
                System.out.println(" > My next move is: " + action);
               
               
                try {
                        sleep(600);
                        sendRequest(action);
                } catch (Exception e) {
                        sendRequest(action);
                }
        }
       
       
        /**
         * Decides what cards to draw.
         */
        private void drawCards() {
                String cardsToChange = "";
               

                if (botChance > 80) {
                        try {
                                sleep(300 + botID * 80);
                        } catch (Exception e) {}

                        sendRequest("dontDrawMyCards");
                        return;

                } else if (Integer.parseInt(botCards[0][1]) > 8) {
                        System.out.println(" > Drawing card: " + botCards[0][1] + "-" + botCards[0][2]);
                        cardsToChange += " 1";
                }

               
                /*
                 * Porownujemy kazda karte z kazda. Jesli porownywana karta ma taka sama
                 * wartosc lub kolor jak wzorzec, ta wymieniamy ta wyzsza, ktora i tak jest usuwana
                 * przy koncowym porownywaniu kart wszystkich uzytkownikow.
                 */
                for (int i = 0; i < 3; i++) {
                        for (int j = i+1; j < 4; j++) {
                                if (botCards[i][1].equalsIgnoreCase(botCards[j][1]) || botCards[i][2].equalsIgnoreCase(botCards[j][2])) {
                                        if (!cardsToChange.contains(botCards[j][0])) {
                                                cardsToChange += " " + botCards[j][0];
                                                System.out.println(" > Drawing card: " + botCards[j][1] + "-" + botCards[j][2]);
                                        }
                                }
                        }
                }
               
                try {
                        sleep(300 + botID * 80);
                } catch (Exception e) {}

                if (cardsToChange != "") {
                        sendRequest("drawMyCards" + cardsToChange);
                } else {
                        System.out.println(" > I don't draw any card.");
                        sendRequest("dontDrawMyCards");
                }
        }
       
       
        /**
         * Calculate BOT's hand strength and max amount of chips
         * BOT can spend in current bet round.
         */
        private void calculateChanceAndMaxBet() {
                botChance = botChance();
       
                if (botChance > 90) {
                        botSuggestedMaxBet = botChips;
                } else {
                        botSuggestedMaxBet = (int)(Math.round(botChips*botChance/100 + 0.5d));
                }
        }
       
       
        /**
         * Calculate strength of BOT's strength.
         * @return Strength as a double value.
         */
        private double botChance() {
                double chance = 0;
                int[] cardsValues = new int[4];
                int[][] hands = new int[4][2];
                String[][] cards = new String[4][3];
                cards = botCards.clone();
               
                for (int k = 0; k <= 1; k++) {
                        String values = "";
                        String shapes = "";
                        int[] numeric = new int[4];
                       
                        for (int i = 0; i <= 3; i++) {
                                int min = 100;
                                int minIndex = -1;
                               
                                for (int j = 0; j <= 3; j++) {
                                        if (cards[j][1] == null)
                                                continue;
                                       
                                        if (values.contains("["+cards[j][1]+"]") || shapes.contains(cards[j][2])) {
                                                cards[j] = new String[3];
                                        } else if (Integer.parseInt(cards[j][1]) < min) {
                                                min = Integer.parseInt(cards[j][1]);
                                                minIndex = j;
                                                
                                        }
                                }
                               
                                if (minIndex == -1) {
                                        values += "[14]";
                                        numeric[i] = 14;
                                        continue;
                                }
                               
                                values += "["+cards[minIndex][1]+"]";
                                numeric[i] = Integer.parseInt(cards[minIndex][1]);
                                shapes += cards[minIndex][2];
                                cards[minIndex] = new String[3];
                        }
                       
                       
                        for (int l = 0; l <= 3; l++) {
                                cards[l] = botCards[3-l];
                        }
                       
                        hands[k] = numeric;
                }
               
                cardsValues = hands[0];
               
                for (int i = 3; i >= 0; i--) {
                        if (hands[0][i] > hands[1][i])
                                cardsValues = hands[1];
                }

               
                int[] best = new int[]{1,2,3,4};
                int[] worst = new int[]{13,14,14,14};
                int nullValues = 0;
               
                for (int i = 0; i <= 3; i++) {
                        if (cardsValues[i] == 14) {
                                nullValues += 1;
                        }
                       

                        chance += ((worst[i] - cardsValues[i])*100) / (worst[i] - best[i]);
                }
               
                chance = Math.round(chance/(4*(nullValues+1)) + 0.5d);
                botChance = chance;
                return chance;
        }
       
       
        /**
         * Decides what move to make if previous move was forbidden.
         * @param act Previous move.
         */
        private void botActionError(String act) {
                String[] actTab = act.split(" ");
                String actCmd = actTab[0];
               
                if (act.equals("allIn"))
                        action = "fold";
                else if (act.equals("call"))
                        action = "allIn";
                else if (act.equals("bet 1"))
                        action = "check";
                else if (actCmd.equals("bet"))
                        action = "bet 1";
                else if (act.equals("raise 1"))
                        action = "call";
                else if (actCmd.equals("raise"))
                        action = "raise 1";
               
                System.out.println(" > My next move is: " + action + " [previous action was forbidden]");
               
                try {
                        sleep(600);
                } catch (Exception e) {}
               
                sendRequest(action);
        }


        /**
         * It's where BOT listens and proceeds responses from server.
         */
        public void run() {
                try {
                        while (true) {
                                String response = reader.readUTF();
                               
                                if (response == null)
                                        continue;
                                       
                                String[] tab = response.split(" ");
                                String cmd = tab[0];


                                if (cmd.equalsIgnoreCase("numberOfPlayers")) {
                                        connectionOk = true;
                                       
                                } else if (cmd.equalsIgnoreCase("yourID")) {
                                        botID = Integer.parseInt(tab[1]);
                                       
                                } else if (cmd.equalsIgnoreCase("yourName")) {
                                        botName = tab[1];

                                } else if (cmd.equalsIgnoreCase("yourCards")) {
                                        botCards = new String[4][3];
                                       
                                        for (int i = 1; i <= 4; i++) {
                                                String[] card = tab[i].split("-");
                                                botCards[i-1] = new String[]{
                                                                i + "",
                                                                card[0],
                                                                card[1] + "-" + card[2] };
                                                
                                            
                                        }
                                       
                                        sortCards();
                                       
                                } else if (cmd.equalsIgnoreCase("yourChips")) {
                                        botChips = Integer.parseInt(tab[1]);
                                       
                                        if (!isGameOpen) {
                                                botChipsOnStart = botChips;
                                                calculateChanceAndMaxBet();
                                        }
                                       
                                } else if (cmd.equalsIgnoreCase("actionOk")) {
                                       
                                        if (action.equalsIgnoreCase("fold")) {
                                                botMovesEnabled = false;
                                               
                                        }
                                       
                                        action = "";
                                       
                                } else if (cmd.equalsIgnoreCase("actionError")) {
                                        botActionError(action);
                                       
                                } else if (cmd.equalsIgnoreCase("startOfTheGame")) {
                                        isGameOpen = true;

                                } else if (cmd.equalsIgnoreCase("nextPlayerMove")) {
                                        if ((tab[1].equalsIgnoreCase(botID + "")) && (botMovesEnabled)) {
                                                botPossibleMoves(tab);
                                                itsBotsMove();
                                        }
                                       
                                } else if (cmd.equalsIgnoreCase("newRound")) {
                                        gameCurrentRound = Integer.parseInt(tab[1]);
                                        gameMaxBet = 0;
                                        botBet = 0;
                                       
                                        if (gameCurrentRound % 2 == 1) {
                                                printMyCards();
                                        }
                                       
                                        if (gameCurrentRound == 1) {
                                                gameMaxBet = Integer.parseInt(tab[8]);
                                               
                                                calculateChanceAndMaxBet();
                                                System.out.println("   My hand strength: " + botChance + "%");
                                               
                                                if (tab[2].equalsIgnoreCase(botID + "")) {
                                                        botPossibleMoves(tab);
                                                        itsBotsMove();
                                                }
                                                       
                                                if (tab[5].equals(botID + ""))
                                                        botBet = Integer.parseInt(tab[6]);
                                                else if (tab[7].equals(botID + ""))
                                                        botBet = Integer.parseInt(tab[8]);
                                               
                                        } else if (gameCurrentRound % 2 == 0) {
                                                drawCards();
                                        } else {
                                                calculateChanceAndMaxBet();
                                               
                                                System.out.println("   My hand strength: " + (int)botChance + "%");
                                                System.out.println("   I have $" + botSuggestedMaxBet + " to spend");
                                        }
                                       
                                } else if (cmd.equalsIgnoreCase("disableMoves")) {
                                        botMovesEnabled = false;
                                        break;
                                       
                                } else if (cmd.equalsIgnoreCase("endOfGame")) {
                                        isGameOpen = false;
                                       
                                        if (tab[1].equalsIgnoreCase("winner")) {
                                                if (tab[2].equals(botID + "")) {
                                                        botChips = Integer.parseInt(tab[4]);
                                                        System.out.println(" *** Yeah, I won! :)");
                                                } else {
                                                        System.out.println(" *** Meeh, maybe next time...");
                                                }
                                                       
                                        } else {
                                                System.out.println(" *** Wow, it's a tie!");
                                        }

                                } else if (cmd.equalsIgnoreCase("playersCanPlayAgain")) {
                                        boolean canIPlay = false;
                                       
                                        for (int i = 1; i < tab.length; i++) {
                                                if (tab[i].equals(botID + "")) {
                                                        canIPlay = true;
                                                        break;  
                                                }
                                        }
                                       
                                        try {
                                                sleep(300 + botID*100);
                                        } catch (Exception e) {}
                                       
                                       
                                        if (canIPlay) {
                                                sendRequest("nextGameYes");
                                        } else {
                                                sendRequest("nextGameNo");
                                                System.exit(0);
                                        }
                                       
                                } else if (cmd.equalsIgnoreCase("tableUpdate")) {

                                        if (tab[1].equals("potAndMaxBet")) {
                                                gameMaxBet = Integer.parseInt(tab[3]);
                                               
                                        } else if (tab[1].equals("yourBet")) {
                                                botTotalBet = Integer.parseInt(tab[2]);
                                                botChips = botChipsOnStart - botTotalBet;
                                        }
                                }
                        }
                } catch (IOException e) {}
        }

       
        /**
         * Displays all BOT's cards in terminal.
         */
        private void printMyCards() {
                System.out.println("   My cards:");
               
                for (int i = 0; i <= 3; i++) {
                        System.out.println(" * " + botCards[i][1] + "-" + botCards[i][2] + " ");
                }
        }
       
       
        /**
         * Sort BOT's cards starting with the strongest card.
         */
        private void sortCards() {
                String[][] sortedCards = new String[4][3];
                int j = 0, minIndex = 0;

                for (int i = 0; i < botCards.length; i++) {
                        int min = 15;
                       
                        for (j = 0; j < botCards.length; j++) {
                                if (botCards[j][0] != null) {
                                        if (Integer.parseInt(botCards[j][1]) < min) {
                                                min = Integer.parseInt(botCards[j][1]);
                                                minIndex = j;
                                                
                                                System.out.println("i= " + i + "j= " + j + "botCards[j][0] = " + botCards[j][0] + "min =" + min + "botCards.length " + botCards.length + "botCards[j][1] " + botCards[j][1] + "minIndex " + minIndex);
                                        }
                                }
                        }
                       
                        sortedCards[i] = botCards[minIndex];
                        botCards[minIndex] = new String[3];
                }
               
                botCards = sortedCards;
        }
       

        /**
         * Main method.
         * @param args Optional parameters: [0] => host, [1] => port.
         */
        public static void main(String[] args) {
                String host;
                int port;

                try {
                        host = args[0];
                        port = Integer.parseInt(args[1]);
                } catch (Exception e) {
                        port = 4437;
                        host = "localhost";
                }

                new Bot(host, port);
        }
}

